#include "rwip_config.h"
//#include "common_log.h"

#if (BLE_APP_SEC)
#include "string.h"
#include "gap.h"
#include "gapc_task.h"
#include "kernel_msg.h"
#include "kernel_task.h"
#include "common_math.h"
#include "common_utils.h"
#include "app_ble.h"

extern struct app_env_tag app_ble_env;

static int app_sec_msg_dflt_handler(kernel_msg_id_t msgid,
				void *param,
				kernel_task_id_t const dest_id,
				kernel_task_id_t const src_id)
{
	return (KERNEL_MSG_CONSUMED);
}

ble_err_t app_sec_load_auth(uint8_t con_idx)
{
	/// TODO:Use nvds or use kv to load ble info
	app_ble_env.connections[con_idx].auth = GAP_AUTH_REQ_NO_MITM_NO_BOND;
	return ERR_SUCCESS;
}

uint8_t app_sec_get_auth(uint8_t con_idx)
{
	BLE_APP_CHECK_CONN_IDX(con_idx);
	if (app_ble_env.connections[con_idx].conhdl != 0xFF) {
		return app_ble_env.connections[con_idx].auth;
	} else {
		bk_printf("conn[%d] do not connected\r\n", con_idx);
		return 0;
	}
}

static int gapc_bond_req_ind_handler(kernel_msg_id_t msgid,
				struct gapc_bond_req_ind const *param,
				kernel_task_id_t const dest_id,
				kernel_task_id_t const src_id)
{
	uint8_t conidx = KERNEL_IDX_GET(src_id);
	uint8_t conn_idx = app_ble_find_conn_idx_handle(conidx);
	
	uint32_t pin_code = (100000 + (common_rand_word() % 900000));
	uint8_t counter;

	struct gapc_bond_cfm *cfm = KERNEL_MSG_ALLOC(GAPC_BOND_CFM,
							src_id, TASK_BLE_APP,
							gapc_bond_cfm);

	switch (param->request) {
	case GAPC_PAIRING_REQ:
		cfm->request = GAPC_PAIRING_RSP;
		if ((app_sec_get_auth(conn_idx) & GAP_AUTH_BOND) == 0) {
			cfm->accept = true;
			cfm->data.pairing_feat.auth = GAP_AUTH_REQ_SEC_CON_BOND;
			cfm->data.pairing_feat.iocap = GAP_IO_CAP_NO_INPUT_NO_OUTPUT;
			cfm->data.pairing_feat.key_size = 16;
			cfm->data.pairing_feat.oob = GAP_OOB_AUTH_DATA_NOT_PRESENT;
			cfm->data.pairing_feat.sec_req = GAP_NO_SEC;
			cfm->data.pairing_feat.ikey_dist = GAP_KDIST_ENCKEY| GAP_KDIST_IDKEY;
			cfm->data.pairing_feat.rkey_dist = GAP_KDIST_ENCKEY| GAP_KDIST_IDKEY;
		} else {
			cfm->accept = false;
		}
		break;
	case GAPC_LTK_EXCH:
		cfm->accept  = true;
		cfm->request = GAPC_LTK_EXCH;

		// Generate all the values
		cfm->data.ltk.ediv = (uint16_t)common_rand_word();

		for (counter = 0; counter < KEY_LEN; counter++) {
			cfm->data.ltk.ltk.key[counter] = (uint8_t)common_rand_word();
		}

		for (counter = 0; counter < RAND_NB_LEN; counter++) {
			cfm->data.ltk.randnb.nb[counter] = (uint8_t)common_rand_word();
		}

		break;
	case GAPC_IRK_EXCH:
		cfm->accept  = true;
		cfm->request = GAPC_IRK_EXCH;

		// Load IRK
		memcpy(cfm->data.irk.irk.key, app_ble_env.loc_irk, KEY_LEN);
		// load device address
		cfm->data.irk.addr.addr_type = ADDR_PUBLIC;
		memcpy(cfm->data.irk.addr.addr.addr, (uint8_t *)&common_default_bdaddr, BD_ADDR_LEN);
		break;
	case GAPC_TK_EXCH:
		cfm->accept  = true;
		cfm->request = GAPC_TK_EXCH;

		// Set the TK value
		memset(cfm->data.tk.key, 0, KEY_LEN);

		cfm->data.tk.key[0] = (uint8_t)((pin_code & 0x000000FF) >>  0);
		cfm->data.tk.key[1] = (uint8_t)((pin_code & 0x0000FF00) >>  8);
		cfm->data.tk.key[2] = (uint8_t)((pin_code & 0x00FF0000) >> 16);
		cfm->data.tk.key[3] = (uint8_t)((pin_code & 0xFF000000) >> 24);
		break;
	default:
		bk_printf("Unknow ble bond request\r\n");
		break;
	}

	// Send the message
	kernel_msg_send(cfm);

	return (KERNEL_MSG_CONSUMED);
}

static int gapc_bond_ind_handler(kernel_msg_id_t msgid,
				struct gapc_bond_ind const *param,
				kernel_task_id_t const dest_id,
				kernel_task_id_t const src_id)
{
	uint8_t conidx = KERNEL_IDX_GET(src_id);
	uint8_t conn_idx = app_ble_find_conn_idx_handle(conidx);

	switch (param->info) {
	case GAPC_PAIRING_SUCCEED:		
		app_ble_env.connections[conn_idx].auth = param->data.auth.info;
		/// TODO:Use nvds or use kv to load ble info
		break;
	case GAPC_REPEATED_ATTEMPT:
		app_ble_disconnect(conn_idx, COMMON_ERROR_REMOTE_USER_TERM_CON);
		break;
	case GAPC_IRK_EXCH:
		/// TODO:Use nvds or use kv to save ble info
		break;
	case GAPC_LTK_EXCH:
		/// TODO:Use nvds or use kv to save ble info
		break;
	case GAPC_PAIRING_FAILED:
		app_ble_disconnect(conn_idx, COMMON_ERROR_REMOTE_USER_TERM_CON);
		break;
	default:
		break;
	}
	return (KERNEL_MSG_CONSUMED);
}

static int gapc_encrypt_req_ind_handler(kernel_msg_id_t msgid,
				struct gapc_encrypt_req_ind const *param,
				kernel_task_id_t const dest_id,
				kernel_task_id_t const src_id)
{
	uint8_t conidx = KERNEL_IDX_GET(src_id);
	uint8_t conn_idx = app_ble_find_conn_idx_handle(conidx);

	struct gapc_encrypt_cfm *cfm = KERNEL_MSG_ALLOC(GAPC_ENCRYPT_CFM,
							src_id, TASK_BLE_APP,
							gapc_encrypt_cfm);

	if ((app_sec_get_auth(conn_idx) & GAP_AUTH_BOND) != 0) {
		cfm->found = true;
		/// TODO:Use nvds or use kv to load ble info
	} else {
		cfm->found = false;
	}
	
	// Send the message
	kernel_msg_send(cfm);

	return (KERNEL_MSG_CONSUMED);
}

static int gapc_encrypt_ind_handler(kernel_msg_id_t msgid,
				void *param,
				kernel_task_id_t const dest_id,
				kernel_task_id_t const src_id)
{
	return (KERNEL_MSG_CONSUMED);
}

const struct kernel_msg_handler app_sec_msg_handler_list[] = 
{
	{KERNEL_MSG_DEFAULT_HANDLER, (kernel_msg_func_t)app_sec_msg_dflt_handler},
	{GAPC_BOND_REQ_IND,          (kernel_msg_func_t)gapc_bond_req_ind_handler},
	{GAPC_BOND_IND,              (kernel_msg_func_t)gapc_bond_ind_handler},
	{GAPC_ENCRYPT_REQ_IND,       (kernel_msg_func_t)gapc_encrypt_req_ind_handler},
	{GAPC_ENCRYPT_IND,           (kernel_msg_func_t)gapc_encrypt_ind_handler},
};

const struct app_subtask_handlers app_sec_handlers = {&app_sec_msg_handler_list[0], ARRAY_LEN(app_sec_msg_handler_list)};
#endif
